/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cl_game.cpp - game module interface
//


#include "client.h"


static etClient_t		etClient;

glqCGame_t *			glqCGame = NULL;


/*
 ==================
 
 ==================
*/
static void CG_EnterGame (){

	cls.state = CS_ACTIVE;

	// Set the initial time and delta
	cl.time = cl.snapshotTime;
	cl.oldTime = cl.snapshotTime;

	cl.timeDelta = cl.snapshotTime - cls.frameTime;

	// TODO: demos

	// Make sure sounds aren't playing
	S_StopAllSounds();

	// Force main menu GUI and console off
	GUI_Close();
	Con_Close();

	// Start capturing voice if needed
	if (cl_voiceChatMode->integerValue == 0)
		S_StartVoiceCapture();

	// TODO: demos

	// Start the background track
	glqCGame->PlayBackgroundTrack();
}

/*
 ==================
 CG_AdjustTimeDelta

 Adjusts the client's view of server time.

 We attempt to have cl.time exactly equal the server's view of time plus
 cl_timeNudge, but with variable latencies over the internet it will often need
 to drift a bit to match conditions.

 Our ideal time would be to have the adjusted time approach, but not pass, the
 very latest snapshot.

 Adjustments are only made when a new snapshot arrives with a rational latency,
 which keeps the adjustment process framerate independent and prevents massive
 overadjustment during times of significant packet loss or bursted delayed
 packets.
 ==================
*/
static void CG_AdjustTimeDelta (){

	int		timeDelta, timeDeltaDelta;

	// The delta never drifts when playing a demo
	if (clc.demoPlaying)
		return;

	// If the current time is way off, just correct to the current value
	timeDelta = cl.snapshotTime - cls.frameTime;
	timeDeltaDelta = glqMath::Abs(timeDelta - cl.timeDelta);

	if (timeDeltaDelta > 500){
		cl.time = cl.snapshotTime;
		cl.oldTime = cl.snapshotTime;

		cl.timeDelta = timeDelta;

		if (cl_showTimeDelta->integerValue)
			Com_Printf("<RESET> ");
	}
	else if (timeDeltaDelta > 100){
		// Fast adjust, cut the difference in half
		cl.timeDelta = (cl.timeDelta + timeDelta) >> 1;

		if (cl_showTimeDelta->integerValue)
			Com_Printf("<FAST> ");
	}
	else {
		// Slow adjust, only drift 1 or 2 milliseconds.
		// If any of the frames between this and the previous snapshot had
		// to be extrapolated, nudge our sense of time back a little.
		// The granularity of -2 / +1 is too high for com_timeScale
		// modified frame times.
		if (com_timeScale->floatValue == 1.0f){
			if (cl.snapshotExtrapolated){
				cl.snapshotExtrapolated = false;

				cl.timeDelta -= 2;
			}
			else
				cl.timeDelta += 1;
		}
	}

	if (cl_showTimeDelta->integerValue)
		Com_Printf("%i\n", cl.timeDelta);
}

/*
 ==================
 
 ==================
*/
void CG_RunFrame (){

	if (!glqCGame)
		return;

	if (cls.state == CS_PRIMED){
		// TODO: demos

		// Getting a valid snapshot ends the connection process
		if (cl.snapshotAvailable){
			cl.snapshotAvailable = false;

			CG_EnterGame();
		}
	}

	if (cls.state != CS_ACTIVE)
		return;

	// Get our current view of time.
	// The cl_timeNudge variable is a debugging aid that allows more or less
	// latency to be added in the interest of better smoothness or better
	// responsiveness.
	cl.time = cls.frameTime + cl.timeDelta - cl_timeNudge->integerValue;

	// Guarantee that time will never flow backwards, even if cl.timeDelta made
	// an adjustment or cl_timeNudge was changed
	if (cl.time < cl.oldTime)
		cl.time = cl.oldTime;

	cl.oldTime = cl.time;

	// Note if we are almost past the latest frame (without cl_timeNudge), so
	// we will try and adjust back a bit when the next snapshot arrives
	if (cls.frameTime + cl.timeDelta >= cl.snapshotTime - 5)
		cl.snapshotExtrapolated = true;

	// If we have gotten new snapshots, drift cl.timeDelta.
	// Don't do this every frame, or a period of packet loss would make a huge
	// adjustment.
	if (cl.snapshotAvailable){
		cl.snapshotAvailable = false;

		CG_AdjustTimeDelta();
	}

	// TODO: demos

	// Run the game simulation
	glqCGame->RunFrame(cl.time, &cl.userCmds[cl.userCmdNumber & USERCMD_MASK]);
}

/*
 ==================
 CG_Redraw
 ==================
*/
void CG_Redraw (){

	if (!glqCGame)
		return;

	// Draw the game view
	glqCGame->Redraw();
}

/*
 ==================
 CG_LoadLevel
 ==================
*/
void CG_LoadLevel (){

	if (!glqCGame)
		return;

	// Load the level
	cls.state = CS_LOADING;

	glqCGame->LoadLevel();

	// Start sending user commands
	cls.state = CS_PRIMED;

	// Clear key and input states
	Key_ClearStates();
	IN_ClearStates();

	// Flush file cache
	FS_FlushCache();

	// Free all temporary allocations
	Mem_FreeAll(TAG_TEMPORARY, true);

	// Make sure everything is paged in
	Mem_TouchMemory();
}

/*
 ==================
 CG_FreeLevel
 ==================
*/
void CG_FreeLevel (){

	if (!glqCGame)
		return;

	// Make sure sounds aren't playing
	S_StopAllSounds();

	// Stop capturing voice
	S_StopVoiceCapture(true);

	// Free the level
	glqCGame->FreeLevel();
}


// ============================================================================


/*
 ==================
 CG_GetUserCmdNumber
 ==================
*/
static int CG_GetUserCmdNumber (){

	return cl.userCmdNumber;
}

/*
 ==================
 CG_GetUserCmd
 ==================
*/
static void CG_GetUserCmd (int userCmdNumber, userCmd_t *userCmd){

	if (userCmdNumber <= cl.userCmdNumber - USERCMD_BACKUP || userCmdNumber > cl.userCmdNumber)
		Com_Error(false, "CG_GetUserCmd: userCmdNumber out of range");

	*userCmd = cl.userCmds[userCmdNumber & USERCMD_MASK];
}

/*
 ==================
 CG_SetJitterAngles
 ==================
*/
static void CG_SetJitterAngles (float pitch, float yaw){

	cl.jitterAngles.Set(pitch, yaw, 0.0f);
}

/*
 ==================
 CG_SetSensitivityScale
 ==================
*/
static void CG_SetSensitivityScale (float sensitivityScale){

	cl.sensitivityScale = sensitivityScale;
}

/*
 ==================
 CG_UpdateLoading
 ==================
*/
static void CG_UpdateLoading (){

	if (cls.state != CS_LOADING)
		return;

	// Process events
	Sys_ProcessEvents();

	// Update the screen
	CL_UpdateScreen();

	// Update sounds
	S_Update(cls.frameTime);
}


/*
 ==============================================================================

 FRAMEWORK

 ==============================================================================
*/


/*
 ==================
 CG_SetupFramework
 ==================
*/
static void CG_SetupFramework (){

	// Common functions
	etClient.glqCommon.Printf							= Com_Printf;
	etClient.glqCommon.DPrintf							= Com_DPrintf;
	etClient.glqCommon.Error							= Com_Error;
	etClient.glqCommon.LaunchEditor					= Com_LaunchEditor;
	etClient.glqCommon.CloseEditor						= Com_CloseEditor;
	etClient.glqCommon.IsEditorActive					= Com_IsEditorActive;

	// Memory functions
	etClient.glqMemory.Alloc							= Mem_Alloc;
	etClient.glqMemory.Alloc16							= Mem_Alloc16;
	etClient.glqMemory.ClearedAlloc					= Mem_ClearedAlloc;
	etClient.glqMemory.ClearedAlloc16					= Mem_ClearedAlloc16;
	etClient.glqMemory.Free							= Mem_Free;
	etClient.glqMemory.FreeAll							= Mem_FreeAll;
	etClient.glqMemory.Size							= Mem_Size;
	etClient.glqMemory.Fill							= Mem_Fill;
	etClient.glqMemory.Copy							= Mem_Copy;
	etClient.glqMemory.Compare							= Mem_Compare;
	etClient.glqMemory.DupString						= Mem_DupString;
	etClient.glqMemory.DupData							= Mem_DupData;

	// File system functions
	etClient.glqFileSystem.Read						= FS_Read;
	etClient.glqFileSystem.Write						= FS_Write;
	etClient.glqFileSystem.Printf						= FS_Printf;
	etClient.glqFileSystem.OpenFile					= FS_OpenFile;
	etClient.glqFileSystem.CloseFile					= FS_CloseFile;
	etClient.glqFileSystem.RemoveFile					= FS_RemoveFile;
	etClient.glqFileSystem.FileExists					= FS_FileExists;
	etClient.glqFileSystem.FindFile					= FS_FindFile;
	etClient.glqFileSystem.ReadFile					= FS_ReadFile;
	etClient.glqFileSystem.FreeFile					= FS_FreeFile;
	etClient.glqFileSystem.WriteFile					= FS_WriteFile;
	etClient.glqFileSystem.ExtractLibrary				= FS_ExtractLibrary;
	etClient.glqFileSystem.ListFiles					= FS_ListFiles;
	etClient.glqFileSystem.ListFilteredFiles			= FS_ListFilteredFiles;
	etClient.glqFileSystem.FreeFileList				= FS_FreeFileList;

	// Command system functions
	etClient.glqCmdSystem.AddCommand					= Cmd_AddCommand;
	etClient.glqCmdSystem.RemoveCommand				= Cmd_RemoveCommand;
	etClient.glqCmdSystem.ArgCompletion_PathExtension	= Cmd_ArgCompletion_PathExtension;
	etClient.glqCmdSystem.ArgCompletion_Definition		= Cmd_ArgCompletion_Definition;
	etClient.glqCmdSystem.ArgCompletion_ConfigName		= Cmd_ArgCompletion_ConfigName;
	etClient.glqCmdSystem.ArgCompletion_DemoName		= Cmd_ArgCompletion_DemoName;
	etClient.glqCmdSystem.ArgCompletion_MapName		= Cmd_ArgCompletion_MapName;
	etClient.glqCmdSystem.ArgCompletion_ModelName		= Cmd_ArgCompletion_ModelName;
	etClient.glqCmdSystem.ArgCompletion_ImageName		= Cmd_ArgCompletion_ImageName;
	etClient.glqCmdSystem.ArgCompletion_SkinName		= Cmd_ArgCompletion_SkinName;
	etClient.glqCmdSystem.ArgCompletion_MaterialName	= Cmd_ArgCompletion_MaterialName;
	etClient.glqCmdSystem.Argc							= Cmd_Argc;
	etClient.glqCmdSystem.Argv							= Cmd_Argv;
	etClient.glqCmdSystem.Args							= Cmd_Args;
	etClient.glqCmdSystem.TokenizeString				= Cmd_TokenizeString;
	etClient.glqCmdSystem.AppendText					= Cmd_AppendText;
	etClient.glqCmdSystem.InsertText					= Cmd_InsertText;
	etClient.glqCmdSystem.ExecuteText					= Cmd_ExecuteText;

	// CVar system functions
	etClient.glqCVarSystem.Register					= CVar_Register;
	etClient.glqCVarSystem.SetBool						= CVar_SetBool;
	etClient.glqCVarSystem.SetInteger					= CVar_SetInteger;
	etClient.glqCVarSystem.SetFloat					= CVar_SetFloat;
	etClient.glqCVarSystem.SetString					= CVar_SetString;
	etClient.glqCVarSystem.UpdateLatched				= CVar_UpdateLatched;
	etClient.glqCVarSystem.FindVariable				= CVar_FindVariable;
	etClient.glqCVarSystem.GetVariableBool				= CVar_GetVariableBool;
	etClient.glqCVarSystem.GetVariableInteger			= CVar_GetVariableInteger;
	etClient.glqCVarSystem.GetVariableFloat			= CVar_GetVariableFloat;
	etClient.glqCVarSystem.GetVariableString			= CVar_GetVariableString;
	etClient.glqCVarSystem.SetVariableBool				= CVar_SetVariableBool;
	etClient.glqCVarSystem.SetVariableInteger			= CVar_SetVariableInteger;
	etClient.glqCVarSystem.SetVariableFloat			= CVar_SetVariableFloat;
	etClient.glqCVarSystem.SetVariableString			= CVar_SetVariableString;
	etClient.glqCVarSystem.AllowCheats					= CVar_AllowCheats;
	etClient.glqCVarSystem.GetModifiedFlags			= CVar_GetModifiedFlags;
	etClient.glqCVarSystem.SetModifiedFlags			= CVar_SetModifiedFlags;
	etClient.glqCVarSystem.ClearModifiedFlags			= CVar_ClearModifiedFlags;
	etClient.glqCVarSystem.InfoString					= CVar_InfoString;

	// Message system functions
	etClient.glqMsgSystem.Init							= MSG_Init;
	etClient.glqMsgSystem.GetWriteState				= MSG_GetWriteState;
	etClient.glqMsgSystem.SetWriteState				= MSG_SetWriteState;
	etClient.glqMsgSystem.ClearWriteState				= MSG_ClearWriteState;
	etClient.glqMsgSystem.RemainingWriteBits			= MSG_RemainingWriteBits;
	etClient.glqMsgSystem.RemainingWriteBytes			= MSG_RemainingWriteBytes;
	etClient.glqMsgSystem.WriteAlign					= MSG_WriteAlign;
	etClient.glqMsgSystem.WriteBits					= MSG_WriteBits;
	etClient.glqMsgSystem.WriteBool					= MSG_WriteBool;
	etClient.glqMsgSystem.WriteChar					= MSG_WriteChar;
	etClient.glqMsgSystem.WriteByte					= MSG_WriteByte;
	etClient.glqMsgSystem.WriteShort					= MSG_WriteShort;
	etClient.glqMsgSystem.WriteWord					= MSG_WriteWord;
	etClient.glqMsgSystem.WriteLong					= MSG_WriteLong;
	etClient.glqMsgSystem.WriteHalf					= MSG_WriteHalf;
	etClient.glqMsgSystem.WriteFloat					= MSG_WriteFloat;
	etClient.glqMsgSystem.WriteAngle					= MSG_WriteAngle;
	etClient.glqMsgSystem.WriteAngle8					= MSG_WriteAngle8;
	etClient.glqMsgSystem.WriteAngle16					= MSG_WriteAngle16;
	etClient.glqMsgSystem.WriteAngles					= MSG_WriteAngles;
	etClient.glqMsgSystem.WriteAngles8					= MSG_WriteAngles8;
	etClient.glqMsgSystem.WriteAngles16				= MSG_WriteAngles16;
	etClient.glqMsgSystem.WriteVec2					= MSG_WriteVec2;
	etClient.glqMsgSystem.WriteVec3					= MSG_WriteVec3;
	etClient.glqMsgSystem.WriteVec4					= MSG_WriteVec4;
	etClient.glqMsgSystem.WriteVec5					= MSG_WriteVec5;
	etClient.glqMsgSystem.WriteNormal					= MSG_WriteNormal;
	etClient.glqMsgSystem.WriteString					= MSG_WriteString;
	etClient.glqMsgSystem.WriteData					= MSG_WriteData;
	etClient.glqMsgSystem.WriteDeltaBits				= MSG_WriteDeltaBits;
	etClient.glqMsgSystem.WriteDeltaChar				= MSG_WriteDeltaChar;
	etClient.glqMsgSystem.WriteDeltaByte				= MSG_WriteDeltaByte;
	etClient.glqMsgSystem.WriteDeltaShort				= MSG_WriteDeltaShort;
	etClient.glqMsgSystem.WriteDeltaWord				= MSG_WriteDeltaWord;
	etClient.glqMsgSystem.WriteDeltaLong				= MSG_WriteDeltaLong;
	etClient.glqMsgSystem.WriteDeltaHalf				= MSG_WriteDeltaHalf;
	etClient.glqMsgSystem.WriteDeltaFloat				= MSG_WriteDeltaFloat;
	etClient.glqMsgSystem.WriteDeltaAngle				= MSG_WriteDeltaAngle;
	etClient.glqMsgSystem.WriteDeltaAngle8				= MSG_WriteDeltaAngle8;
	etClient.glqMsgSystem.WriteDeltaAngle16			= MSG_WriteDeltaAngle16;
	etClient.glqMsgSystem.WriteDeltaAngles				= MSG_WriteDeltaAngles;
	etClient.glqMsgSystem.WriteDeltaAngles8			= MSG_WriteDeltaAngles8;
	etClient.glqMsgSystem.WriteDeltaAngles16			= MSG_WriteDeltaAngles16;
	etClient.glqMsgSystem.WriteDeltaVec2				= MSG_WriteDeltaVec2;
	etClient.glqMsgSystem.WriteDeltaVec3				= MSG_WriteDeltaVec3;
	etClient.glqMsgSystem.WriteDeltaVec4				= MSG_WriteDeltaVec4;
	etClient.glqMsgSystem.WriteDeltaVec5				= MSG_WriteDeltaVec5;
	etClient.glqMsgSystem.WriteDeltaNormal				= MSG_WriteDeltaNormal;
	etClient.glqMsgSystem.GetReadState					= MSG_GetReadState;
	etClient.glqMsgSystem.SetReadState					= MSG_SetReadState;
	etClient.glqMsgSystem.ClearReadState				= MSG_ClearReadState;
	etClient.glqMsgSystem.RemainingReadBits			= MSG_RemainingReadBits;
	etClient.glqMsgSystem.RemainingReadBytes			= MSG_RemainingReadBytes;
	etClient.glqMsgSystem.ReadAlign					= MSG_ReadAlign;
	etClient.glqMsgSystem.ReadBits						= MSG_ReadBits;
	etClient.glqMsgSystem.ReadBool						= MSG_ReadBool;
	etClient.glqMsgSystem.ReadChar						= MSG_ReadChar;
	etClient.glqMsgSystem.ReadByte						= MSG_ReadByte;
	etClient.glqMsgSystem.ReadShort					= MSG_ReadShort;
	etClient.glqMsgSystem.ReadWord						= MSG_ReadWord;
	etClient.glqMsgSystem.ReadLong						= MSG_ReadLong;
	etClient.glqMsgSystem.ReadHalf						= MSG_ReadHalf;
	etClient.glqMsgSystem.ReadFloat					= MSG_ReadFloat;
	etClient.glqMsgSystem.ReadAngle					= MSG_ReadAngle;
	etClient.glqMsgSystem.ReadAngle8					= MSG_ReadAngle8;
	etClient.glqMsgSystem.ReadAngle16					= MSG_ReadAngle16;
	etClient.glqMsgSystem.ReadAngles					= MSG_ReadAngles;
	etClient.glqMsgSystem.ReadAngles8					= MSG_ReadAngles8;
	etClient.glqMsgSystem.ReadAngles16					= MSG_ReadAngles16;
	etClient.glqMsgSystem.ReadVec2						= MSG_ReadVec2;
	etClient.glqMsgSystem.ReadVec3						= MSG_ReadVec3;
	etClient.glqMsgSystem.ReadVec4						= MSG_ReadVec4;
	etClient.glqMsgSystem.ReadVec5						= MSG_ReadVec5;
	etClient.glqMsgSystem.ReadNormal					= MSG_ReadNormal;
	etClient.glqMsgSystem.ReadString					= MSG_ReadString;
	etClient.glqMsgSystem.ReadData						= MSG_ReadData;
	etClient.glqMsgSystem.ReadDeltaBits				= MSG_ReadDeltaBits;
	etClient.glqMsgSystem.ReadDeltaChar				= MSG_ReadDeltaChar;
	etClient.glqMsgSystem.ReadDeltaByte				= MSG_ReadDeltaByte;
	etClient.glqMsgSystem.ReadDeltaShort				= MSG_ReadDeltaShort;
	etClient.glqMsgSystem.ReadDeltaWord				= MSG_ReadDeltaWord;
	etClient.glqMsgSystem.ReadDeltaLong				= MSG_ReadDeltaLong;
	etClient.glqMsgSystem.ReadDeltaHalf				= MSG_ReadDeltaHalf;
	etClient.glqMsgSystem.ReadDeltaFloat				= MSG_ReadDeltaFloat;
	etClient.glqMsgSystem.ReadDeltaAngle				= MSG_ReadDeltaAngle;
	etClient.glqMsgSystem.ReadDeltaAngle8				= MSG_ReadDeltaAngle8;
	etClient.glqMsgSystem.ReadDeltaAngle16				= MSG_ReadDeltaAngle16;
	etClient.glqMsgSystem.ReadDeltaAngles				= MSG_ReadDeltaAngles;
	etClient.glqMsgSystem.ReadDeltaAngles8				= MSG_ReadDeltaAngles8;
	etClient.glqMsgSystem.ReadDeltaAngles16			= MSG_ReadDeltaAngles16;
	etClient.glqMsgSystem.ReadDeltaVec2				= MSG_ReadDeltaVec2;
	etClient.glqMsgSystem.ReadDeltaVec3				= MSG_ReadDeltaVec3;
	etClient.glqMsgSystem.ReadDeltaVec4				= MSG_ReadDeltaVec4;
	etClient.glqMsgSystem.ReadDeltaVec5				= MSG_ReadDeltaVec5;
	etClient.glqMsgSystem.ReadDeltaNormal				= MSG_ReadDeltaNormal;

	// Definition manager functions
	etClient.glqDefManager.RegisterType				= Def_RegisterType;
	etClient.glqDefManager.LoadDefs					= Def_LoadDefs;
	etClient.glqDefManager.PurgeDefs					= Def_PurgeDefs;
	etClient.glqDefManager.EnumDefs					= Def_EnumDefs;
	etClient.glqDefManager.GetNumDefs					= Def_GetNumDefs;
	etClient.glqDefManager.GetDefByIndex				= Def_GetDefByIndex;
	etClient.glqDefManager.FindDef						= Def_FindDef;

	// Parser functions
	etClient.glqParser.ReadToken						= PS_ReadToken;
	etClient.glqParser.UnreadToken						= PS_UnreadToken;
	etClient.glqParser.ReadBool						= PS_ReadBool;
	etClient.glqParser.ReadInteger						= PS_ReadInteger;
	etClient.glqParser.ReadFloat						= PS_ReadFloat;
	etClient.glqParser.ReadMatrix1D					= PS_ReadMatrix1D;
	etClient.glqParser.ReadMatrix2D					= PS_ReadMatrix2D;
	etClient.glqParser.ReadMatrix3D					= PS_ReadMatrix3D;
	etClient.glqParser.ExpectTokenString				= PS_ExpectTokenString;
	etClient.glqParser.ExpectTokenType					= PS_ExpectTokenType;
	etClient.glqParser.CheckTokenString				= PS_CheckTokenString;
	etClient.glqParser.CheckTokenType					= PS_CheckTokenType;
	etClient.glqParser.PeekTokenString					= PS_PeekTokenString;
	etClient.glqParser.PeekTokenType					= PS_PeekTokenType;
	etClient.glqParser.TokenAvailable					= PS_TokenAvailable;
	etClient.glqParser.SkipUntilString					= PS_SkipUntilString;
	etClient.glqParser.SkipRestOfLine					= PS_SkipRestOfLine;
	etClient.glqParser.SkipBracedSection				= PS_SkipBracedSection;
	etClient.glqParser.ScriptWarning					= PS_ScriptWarning;
	etClient.glqParser.ScriptError						= PS_ScriptError;
	etClient.glqParser.ResetScript						= PS_ResetScript;
	etClient.glqParser.EndOfScript						= PS_EndOfScript;
	etClient.glqParser.SetScriptFlags					= PS_SetScriptFlags;
	etClient.glqParser.SetPunctuationsTable			= PS_SetPunctuationsTable;
	etClient.glqParser.LoadScriptFile					= PS_LoadScriptFile;
	etClient.glqParser.LoadScriptMemory				= PS_LoadScriptMemory;
	etClient.glqParser.FreeScript						= PS_FreeScript;

	// System functions
	etClient.glqSystem.ListFiles						= Sys_ListFiles;
	etClient.glqSystem.ListFilteredFiles				= Sys_ListFilteredFiles;
	etClient.glqSystem.FreeFileList					= Sys_FreeFileList;
	etClient.glqSystem.CreateDirectory					= Sys_CreateDirectory;
	etClient.glqSystem.RemoveDirectory					= Sys_RemoveDirectory;
	etClient.glqSystem.CurrentDirectory				= Sys_CurrentDirectory;
	etClient.glqSystem.DefaultBaseDirectory			= Sys_DefaultBaseDirectory;
	etClient.glqSystem.DefaultSaveDirectory			= Sys_DefaultSaveDirectory;
	etClient.glqSystem.DiskFreeSpace					= Sys_DiskFreeSpace;
	etClient.glqSystem.LoadLibrary						= Sys_LoadLibrary;
	etClient.glqSystem.FreeLibrary						= Sys_FreeLibrary;
	etClient.glqSystem.GetProcAddress					= Sys_GetProcAddress;
	etClient.glqSystem.GetProcessorId					= Sys_GetProcessorId;
	etClient.glqSystem.GetProcessorCount				= Sys_GetProcessorCount;
	etClient.glqSystem.GetProcessorString				= Sys_GetProcessorString;
	etClient.glqSystem.DebugBreak						= Sys_DebugBreak;
	etClient.glqSystem.Sleep							= Sys_Sleep;
	etClient.glqSystem.Milliseconds					= Sys_Milliseconds;
	etClient.glqSystem.ClockTicks						= Sys_ClockTicks;
	etClient.glqSystem.ClockTicksPerSecond				= Sys_ClockTicksPerSecond;
	etClient.glqSystem.GetInstanceHandle				= Sys_GetInstanceHandle;
	etClient.glqSystem.GetIconHandle					= Sys_GetIconHandle;
	etClient.glqSystem.GetClipboardText				= Sys_GetClipboardText;
	etClient.glqSystem.SetClipboardText				= Sys_SetClipboardText;
	etClient.glqSystem.StartProcess					= Sys_StartProcess;
	etClient.glqSystem.OpenURL							= Sys_OpenURL;

	// Table functions
	etClient.glqTable.FindTable						= LUT_FindTable;
	etClient.glqTable.LookupTable						= LUT_LookupTable;

	// Collision functions
	etClient.glqCollision.LoadMap						= CM_LoadMap;
	etClient.glqCollision.EntityString					= CM_EntityString;
	etClient.glqCollision.LeafPVS						= CM_LeafPVS;
	etClient.glqCollision.PointInLeaf					= CM_PointInLeaf;

	// Renderer functions
	etClient.glqRenderer.LoadMap						= R_LoadMap;
	etClient.glqRenderer.LoadPostProcess				= R_LoadPostProcess;
	etClient.glqRenderer.RegisterModel					= R_RegisterModel;
	etClient.glqRenderer.RegisterMaterial				= R_RegisterMaterial;
	etClient.glqRenderer.RegisterMaterialLight			= R_RegisterMaterialLight;
	etClient.glqRenderer.RegisterMaterialNoMip			= R_RegisterMaterialNoMip;

	etClient.glqRenderer.ModelFrames					= R_ModelFrames;

	etClient.glqRenderer.AllocScene					= R_AllocScene;
	etClient.glqRenderer.FreeScene						= R_FreeScene;
	etClient.glqRenderer.AddRenderEntity				= R_AddRenderEntity;
	etClient.glqRenderer.UpdateRenderEntity			= R_UpdateRenderEntity;
	etClient.glqRenderer.RemoveRenderEntity			= R_RemoveRenderEntity;
	etClient.glqRenderer.AddRenderLight				= R_AddRenderLight;
	etClient.glqRenderer.UpdateRenderLight				= R_UpdateRenderLight;
	etClient.glqRenderer.RemoveRenderLight				= R_RemoveRenderLight;
	etClient.glqRenderer.AddRenderParticle				= R_AddRenderParticle;
	etClient.glqRenderer.UpdateRenderParticle			= R_UpdateRenderParticle;
	etClient.glqRenderer.RemoveRenderParticle			= R_RemoveRenderParticle;

	etClient.glqRenderer.PrecacheLights				= R_PrecacheLights;
	etClient.glqRenderer.RenderScene					= R_RenderScene;
	etClient.glqRenderer.CaptureRenderToTexture		= R_CaptureRenderToTexture;
	etClient.glqRenderer.UpdateTextureImage			= R_UpdateTextureImage;
	etClient.glqRenderer.SetColor						= R_SetColor;
	etClient.glqRenderer.SetColor1						= R_SetColor1;
	etClient.glqRenderer.SetColor2						= R_SetColor2;
	etClient.glqRenderer.SetColor3						= R_SetColor3;
	etClient.glqRenderer.SetColor4						= R_SetColor4;
	etClient.glqRenderer.SetParameter					= R_SetParameter;
	etClient.glqRenderer.SetParameters					= R_SetParameters;
	etClient.glqRenderer.DrawStretchPic				= R_DrawStretchPic;
	etClient.glqRenderer.DrawStretchPicEx				= R_DrawStretchPicEx;
	etClient.glqRenderer.DrawChar						= R_DrawChar;
	etClient.glqRenderer.DrawString					= R_DrawString;
	etClient.glqRenderer.CropRenderSize				= R_CropRenderSize;
	etClient.glqRenderer.UnCropRenderSize				= R_UnCropRenderSize;
	etClient.glqRenderer.DebugAxis						= R_DebugAxis;
	etClient.glqRenderer.DebugLine						= R_DebugLine;
	etClient.glqRenderer.DebugArrow					= R_DebugArrow;
	etClient.glqRenderer.DebugBounds					= R_DebugBounds;
	etClient.glqRenderer.DebugSphere					= R_DebugSphere;
	etClient.glqRenderer.DebugBox						= R_DebugBox;
	etClient.glqRenderer.DebugCone						= R_DebugCone;
	etClient.glqRenderer.DebugCircle					= R_DebugCircle;
	etClient.glqRenderer.DebugFrustum					= R_DebugFrustum;
	etClient.glqRenderer.DebugPolygon					= R_DebugPolygon;
	etClient.glqRenderer.DebugText						= R_DebugText;
	etClient.glqRenderer.GetGLConfig					= R_GetGLConfig;

	// Sound functions
	etClient.glqSound.RegisterSoundShader				= S_RegisterSoundShader;
	etClient.glqSound.AddSoundEmitter					= S_AddSoundEmitter;
	etClient.glqSound.UpdateSoundEmitter				= S_UpdateSoundEmitter;
	etClient.glqSound.RemoveSoundEmitter				= S_RemoveSoundEmitter;
	etClient.glqSound.PlaySoundFromEmitter				= S_PlaySoundFromEmitter;
	etClient.glqSound.StopSoundFromEmitter				= S_StopSoundFromEmitter;
	etClient.glqSound.AmplitudeFromEmitter				= S_AmplitudeFromEmitter;
	etClient.glqSound.PlayingFromEmitter				= S_PlayingFromEmitter;
	etClient.glqSound.ClearAllSoundEmitters			= S_ClearAllSoundEmitters;
	etClient.glqSound.PlaceListener					= S_PlaceListener;
	etClient.glqSound.ShakeAmplitudeForListener		= S_ShakeAmplitudeForListener;
	etClient.glqSound.PlayLocalSound					= S_PlayLocalSound;
	etClient.glqSound.PlayMusic						= S_PlayMusic;
	etClient.glqSound.FadeMusic						= S_FadeMusic;
	etClient.glqSound.StopMusic						= S_StopMusic;
	etClient.glqSound.GetALConfig						= S_GetALConfig;

	// GUI functions
	etClient.glqGUI.Register							= GUI_Register;
	etClient.glqGUI.Activate							= GUI_Activate;

	// Client functions
	etClient.GetUserCmdNumber							= CG_GetUserCmdNumber;
	etClient.GetUserCmd								= CG_GetUserCmd;
	etClient.SetJitterAngles							= CG_SetJitterAngles;
	etClient.SetSensitivityScale						= CG_SetSensitivityScale;
	etClient.UpdateLoading								= CG_UpdateLoading;
}


/*
 ==============================================================================

 INITIALIZATION & SHUTDOWN

 ==============================================================================
*/


/*
 ==================
 CG_Init
 ==================
*/
void CG_Init (){

	char		path[MAX_PATH_LENGTH];
	GETCGAMEAPI	GetCGameAPI;

	// Set up the framework
	CG_SetupFramework();

	// Extract the game module from a pack file
	if (!FS_ExtractLibrary("CGame", path))
		Com_Error(true, "Could not extract client game module");

	// Load the game module
	Com_Printf("Loading client game module\n");

	cls.gameLibHandle = Sys_LoadLibrary(path);
	if (!cls.gameLibHandle)
		Com_Error(true, "Could not load client game module");

	// Get the game API
	GetCGameAPI = (GETCGAMEAPI)Sys_GetProcAddress(cls.gameLibHandle, "GetCGameAPI");
	if (!GetCGameAPI)
		Com_Error(true, "Could not get client game interface");

	glqCGame = GetCGameAPI(&etClient);

	if (glqCGame->apiVersion != CGAME_API_VERSION)
		Com_Error(true, "Client game interface version is %i, expected version %i", glqCGame->apiVersion, CGAME_API_VERSION);

	// Initialize the game
	glqCGame->Init(clc.demoPlaying, cl.clientNum, clc.serverMessageSequence);
}

/*
 ==================
 CG_Shutdown
 ==================
*/
void CG_Shutdown (){

	if (!cls.gameLibHandle)
		return;

	// Shutdown the game
	if (glqCGame){
		glqCGame->Shutdown();
		glqCGame = NULL;
	}

	// Free all game allocations
	Mem_FreeAll(TAG_CLIENT, true);

	// Free the game module
	Com_Printf("Unloading client game module\n");

	Sys_FreeLibrary(cls.gameLibHandle);
	cls.gameLibHandle = 0;
}